package com.storydo.file.share.utils;

import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import com.storydo.file.share.exception.BaseException;

/**   
* @Title: NetUtil.java 
* @Description: TODO
* @author : Storydo
* @date: 2019年8月8日 上午11:00:42 
* @version:V1.0 
* Copyright 悦享互联 2016 All right reserved.
* Modification  History:
* Version       Date          Author          Description
* ----------------------------------------------------------------------------
*  1.0         2019年8月8日        Storydo              TODO
*/

public class NetUtil {

	private static Logger logs = LoggerFactory.getLogger(NetUtil.class); 
	
	public static boolean ipIsReachable(String ip)  {
		if(Stringer.isNullOrEmpty(ip))
			throw new BaseException("ip地址为空!");
		
		try {
			InetAddress add = InetAddress.getByName(ip);
			return add.isReachable(2000);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new BaseException(e.getMessage());
		}
	}

	/**
	 * 
	* @Description: 本机的ipv4地址
	* @return
	* @author: Storydo
	* @date: 2021年10月19日 下午5:45:33
	 */
	public static String localIPv4() {
		String ip = null;
		for (InetAddress address : localInetAddresses()) {
			if (!address.isLoopbackAddress() && address.getHostAddress().indexOf(":") == -1) {
				ip = address.getHostAddress();
				break;
			}
		}
		return ip;
	}
	
	
	/**
	 * 
	* @Description: 获取本机所有网卡上的地址，包括v4和v6
	* @return
	* @author: Storydo
	* @date: 2021年10月19日 下午5:44:26
	 */
	public static List<InetAddress> localInetAddresses() {
		List<InetAddress> ipList = new ArrayList<>();
		try {
			Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
			while(nis.hasMoreElements()) {
				NetworkInterface ni=nis.nextElement();
				Enumeration<InetAddress> address = ni.getInetAddresses();
				while (address.hasMoreElements()) {
					ipList.add(address.nextElement());
				}
				
			}
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logs.error(e.getMessage(), e);
		}
		return ipList;
	}
	
	public static PingResult ping(String host) {
		return ping(host, 5, 6, 1);
	}
	
	public static PingResult ping(String host, int count, int timeout, float interval) {
		PingResult result = null;
		BufferedReader br = null;
		try {
			String os = System.getProperty("os.name", "Linux");
			String command = String.format("ping %s -c %d -i %f", host, count, interval);
			if (os.contains("Linux")) {
				command += " -w " + timeout; 
			} else {
				command +=" -t " + timeout;
			}
			Process process = Runtime.getRuntime().exec(command);
			br = new BufferedReader(new InputStreamReader(process.getInputStream()));
			String line = null;
			result = new NetUtil.PingResult();
			while ((line = br.readLine()) != null) {
//				System.out.println(line);
				// 丢包率
				if (line.contains("packet loss")) {
					result.setLoss(parseLoss(line));
				}
				// 统计结果
				if (line.contains("min/avg/max")) {
					parseStatistics(line, result);
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logs.error(e.getMessage(), e);
			return null;
		}finally {
			try {
				if (br != null) {
					br.close();
				}
			} catch (Exception e2) {
				// TODO: handle exception
			}
		}
		return result;
	}
	
	private static void parseStatistics(String str, PingResult result) throws Exception {
		List<List<String>> list = Stream.of(str.split(" ")).filter(s -> !Stringer.isNullOrEmpty(s) && s.contains("/"))
				.map(s -> Arrays.asList(s.split("/"))).collect(Collectors.toList());
		Map<String, String> map = new HashMap<>();
		List<String> keys = list.get(0);
		List<String> values = list.get(1);
		for (int i = 0; i < list.get(0).size(); i++) {
			String key = keys.get(i);
			map.put(key, values.get(i));
			if (key.contains("dev")) {
				map.put("dev", values.get(i));
			}
		}
		
		for (String key : map.keySet()) {
			PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(PingResult.class, key);
			if (pd != null) {
				pd.getWriteMethod().invoke(result, Float.parseFloat(map.get(key)));
			}
		}
	}
	
	private static Float parseLoss(String str) {
		Pattern p = Pattern.compile(".*, (\\d*.?\\d*)% packet loss.*");
		Matcher matcher = p.matcher(str);
		if (matcher.find()) {
			Float loss = new Float(matcher.group(1));
			return loss;
		}
		return null;
	}
	
	public static HttpResult curl(String url) {
		return curl(url, 3);
	}
	
	
	public static HttpResult curl(String url, int timeout) {
		HttpResult result = null;
		BufferedReader br = null;
		try {
			String cmd = String.format("curl -I -m %d -s -w %%{http_code}:%%{time_total} -o /dev/null %s", timeout, url);
			Process p = Runtime.getRuntime().exec(cmd);
			br = new BufferedReader(new InputStreamReader(p.getInputStream()));
			String line = null; result = new HttpResult();
			while((line = br.readLine()) != null) {
				if (line.contains(":")) {
					String[] strs = line.split(":");
					result.setCode(Integer.parseInt(strs[0]));
					result.setTimeTotal(Float.parseFloat(strs[1])*1000);
				}
			}
		} catch (Exception e) {
			logs.error(e.getMessage(), e);
			return null;
		}finally {
			try {
				if (br!=null) {
					br.close();
				}
			} catch (Exception e) {
				
			}
		}
		return result;
	}
	
	
	public static class HttpResult {
		
		private Integer code;
		// 毫秒
		private Float timeTotal;

		
		public boolean isSuccessful() {
			return code!=null && code.intValue() == 200;
		}
		
		public Integer getCode() {
			return code;
		}

		public void setCode(Integer code) {
			this.code = code;
		}

		public Float getTimeTotal() {
			return timeTotal;
		}

		public void setTimeTotal(Float timeTotal) {
			this.timeTotal = timeTotal;
		}
		
		
		
		
	}
	
	
	public static class PingResult {
		// 丢包率
		private Float loss;
		// 最小值 /ms
		private Float min;
		//平均值 /ms
		private Float avg;
		
		// 最大值 /ms
		private Float max;
		
		// 类似数学标准差 越小代表网络越稳定
		private Float dev;

		public Float getLoss() {
			return loss;
		}

		public void setLoss(Float loss) {
			this.loss = loss;
		}

		public Float getMin() {
			return min;
		}

		public void setMin(Float min) {
			this.min = min;
		}

		public Float getAvg() {
			return avg;
		}

		public void setAvg(Float avg) {
			this.avg = avg;
		}

		public Float getMax() {
			return max;
		}

		public void setMax(Float max) {
			this.max = max;
		}

		public Float getDev() {
			return dev;
		}

		public void setDev(Float dev) {
			this.dev = dev;
		}	
	}
	
	public static long parseIpv4FromStr(String ip) {
		long val = 0;
		for (String element : ip.trim().split("\\.")) {
			val = (val << 8 ) | Integer.parseInt(element);
		}
		return val;
	}
	
	public static String ipv4ToString(long ip) {
		String[] eles = new String[4];
		for (int index = 0; index < 4 ; index ++) {
			eles[index] =String.format("%d", ip >> (3 - index) * 8 & 0xff);
		}
		return String.join(".", eles);
	}
	
}
